﻿using System;
using System.IO;
using ServiceStack.Logging;

namespace ServiceStack.Razor.Managers
{
    public interface ILiveReload
    {
        void StartWatching(string scanRootPath);
    }

    public class FileSystemWatcherLiveReload : ILiveReload
    {
        public static ILog Log = LogManager.GetLogger(typeof(FileSystemWatcherLiveReload));

        /// <summary>
        /// The purpose of the FileSystemWatcher is to ensure razor pages are
        /// consistent with the code generated by the razor engine. The file
        /// system watcher will invalidate pages and queue them for recompilation.
        /// </summary>
        protected FileSystemWatcher FileSystemWatcher;
        private readonly RazorViewManager views;

        public FileSystemWatcherLiveReload(RazorViewManager views)
        {
            this.views = views;
        }

        public void StartWatching(string scanRootPath)
        {
            //setup the file system watcher for page invalidation
            this.FileSystemWatcher = new FileSystemWatcher(scanRootPath, "*.*")
                {
                    IncludeSubdirectories = true,
                    EnableRaisingEvents = true,
                    //NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.FileName | NotifyFilters.LastWrite
                };

            this.FileSystemWatcher.Changed += FileSystemWatcher_Changed;
            this.FileSystemWatcher.Created += FileSystemWatcher_Created;
            this.FileSystemWatcher.Deleted += FileSystemWatcher_Deleted;
            this.FileSystemWatcher.Renamed += FileSystemWatcher_Renamed;
            this.FileSystemWatcher.Error += FileSystemWatcher_Error;
        }

        /// <summary>
        /// Avoid throwing unhandled exception when shutting down ASP.NET host
        /// </summary>
        private void FileSystemWatcher_Error(object sender, ErrorEventArgs e)
        {
            Log.WarnFormat("FileSystemWatcher Error: ", sender);
        }

        protected virtual void FileSystemWatcher_Renamed(object sender, RenamedEventArgs e)
        {
            try
            {
                var oldPagePath = views.GetDictionaryPagePath(views.GetRelativePath(e.OldFullPath));

                if (!views.Pages.Remove(oldPagePath))
                {
                    //Debugger.Break();
                }

                views.AddPage(e.FullPath);
            }
            catch (Exception ex)
            {
                Log.Warn("FileSystemWatcher_Renamed error: ", ex);
            }
        }

        protected virtual void FileSystemWatcher_Deleted(object sender, FileSystemEventArgs e)
        {
            try
            {
                var file = views.GetVirtualFile(e.FullPath);
                if (file == null || !views.IsWatchedFile(file)) return;

                var pathPage = views.GetDictionaryPagePath(file);

                views.Pages.Remove(pathPage);
            }
            catch (Exception ex)
            {
                Log.Warn("FileSystemWatcher_Deleted error: ", ex);
            }
        }

        protected virtual void FileSystemWatcher_Created(object sender, FileSystemEventArgs e)
        {
            try
            {
                views.AddPage(e.FullPath);
            }
            catch (Exception ex)
            {
                Log.Warn("FileSystemWatcher_Created error: ", ex);
            }
        }

        protected virtual void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            try
            {
                var file = views.GetVirtualFile(e.FullPath);
                if (file == null || !views.IsWatchedFile(file)) return;

                RazorPage page = views.GetPage(file);
                if (page != null)
                    views.InvalidatePage(page);
            }
            catch (Exception ex)
            {
                Log.Warn("FileSystemWatcher_Changed error: ", ex);
            }
        }
    }
}
